SOLID principles in C# with short examples
1. S - Single Responsibility Principle (SRP)
Principle: A class should have only one reason to change.
// Bad: Mixing responsibilities public class Invoice { public void CalculateTotal() { /*...*/ } public void PrintInvoice() { /*...*/ } // Change if printing logic changes public void SaveToDatabase() { /*...*/ } // Change if database logic changes } // Good: Separate responsibilities public class Invoice { public void CalculateTotal() { /*...*/ } } public class InvoicePrinter { public void Print(Invoice invoice) { /*...*/ } } public class InvoiceRepository { public void Save(Invoice invoice) { /*...*/ } }
2. O - Open/Closed Principle (OCP)
Principle: Open for extension, closed for modification.
// Bad: Modifying existing code for new shapes public class AreaCalculator { public double CalculateArea(object shape) { if (shape is Rectangle) { /* Calculate rectangle */ } else if (shape is Circle) { /* Calculate circle */ } // MODIFY when adding a new shape } } // Good: Extend without modifying existing code public abstract class Shape { public abstract double CalculateArea(); } public class Rectangle : Shape { public override double CalculateArea() { /* ... */ } } public class Circle : Shape { public override double CalculateArea() { /* ... */ } } // New shapes extend Shape without changing AreaCalculator
3. L - Liskov Substitution Principle (LSP)
Principle: Subtypes must be substitutable for their base types.
// Bad: Square breaks behavior of Rectangle public class Rectangle { public virtual int Width { get; set; } public virtual int Height { get; set; } } public class Square : Rectangle { public override int Width { set { base.Width = base.Height = value; } } // Violates LSP } // Good: Separate base class or interface public abstract class Shape { /* ... */ } public class Rectangle : Shape { /* ... */ } public class Square : Shape { /* ... */ }
4. I - Interface Segregation Principle (ISP)
Principle: Clients shouldn’t depend on interfaces they don’t use.
// Bad: Monolithic interface public interface IWorker { void Work(); void Eat(); // Not all workers need this } // Good: Split interfaces public interface IWorkable { void Work(); } public interface IEatable { void Eat(); } public class Robot : IWorkable // Robot doesn't eat { public void Work() { /* ... */ } }
5. D - Dependency Inversion Principle (DIP)
Principle: Depend on abstractions, not concretions.
// Bad: Direct dependency on concrete class public class WeatherApi { public string GetWeather() => "Sunny"; } public class WeatherReporter { private WeatherApi _api = new WeatherApi(); // Tight coupling public string Report() => _api.GetWeather(); } // Good: Depend on abstraction public interface IWeatherProvider { string GetWeather(); } public class WeatherReporter { private readonly IWeatherProvider _provider; public WeatherReporter(IWeatherProvider provider) // Dependency injection { _provider = provider; } public string Report() => _provider.GetWeather(); }
Interview Tips:
SRP: "One class, one job."
OCP: "Extend behavior without changing code."
LSP: "Subclasses should enhance, not break, base class behavior."
ISP: "Keep interfaces small and focused."
DIP: "Use abstractions to decouple layers."
Comments
Post a Comment